From a40440c6844367eecd42f5043d9904e5f5b50d2f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 19 Feb 2016 00:07:22 -0800 Subject: [PATCH] Read configuration from environment variables This commit adds a more principled system to rationalize what ends up being a configuration value versus an environment variable. This problem is solved by just saying that they're one and the same! Similar to Bundler, this commit supports overriding the `foo.bar` configuration value with the `CARGO_FOO_BAR` environment variable. Currently this is used as part of the `get_string` and `get_i64` methods on `Config`. This means, for example, that the following environment variables can now be used to configure Cargo: * CARGO_BUILD_JOBS * CARGO_HTTP_TIMEOUT * CARGO_HTTP_PROXY Currently it's not supported to encode a list in an environment variable, so for example `CARGO_PATHS` would not be read when reading the global `paths` configuration value. cc #2362 cc #2395 -- intended to close this in tandem with #2397 --- src/cargo/util/config.rs | 33 +++++++++++++++++++++++++++++++-- src/cargo/util/errors.rs | 10 ++++++++++ src/doc/config.md | 12 ++++++++++-- tests/test_cargo_config.rs | 26 ++++++++++++++++++++++++++ tests/tests.rs | 1 + 5 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 tests/test_cargo_config.rs diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index b2361e74d..00df39b70 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -7,12 +7,13 @@ use std::fs::{self, File}; use std::io::prelude::*; use std::mem; use std::path::{Path, PathBuf}; +use std::str::FromStr; use rustc_serialize::{Encodable,Encoder}; use toml; use core::shell::{Verbosity, ColorConfig}; use core::{MultiShell, Package}; -use util::{CargoResult, ChainError, Rustc, internal, human, paths}; +use util::{CargoResult, CargoError, ChainError, Rustc, internal, human, paths}; use util::toml as cargo_toml; @@ -148,7 +149,29 @@ impl Config { Ok(Some(val.clone())) } + fn get_env(&self, key: &str) -> CargoResult>> + where Box: From + { + let key = key.replace(".", "_") + .replace("-", "_") + .chars() + .flat_map(|c| c.to_uppercase()) + .collect::(); + match env::var(&format!("CARGO_{}", key)) { + Ok(value) => { + Ok(Some(Value { + val: try!(value.parse()), + definition: Definition::Environment, + })) + } + Err(..) => Ok(None), + } + } + pub fn get_string(&self, key: &str) -> CargoResult>> { + if let Some(v) = try!(self.get_env(key)) { + return Ok(Some(v)) + } match try!(self.get(key)) { Some(CV::String(i, path)) => { Ok(Some(Value { @@ -209,6 +232,9 @@ impl Config { } pub fn get_i64(&self, key: &str) -> CargoResult>> { + if let Some(v) = try!(self.get_env(key)) { + return Ok(Some(v)) + } match try!(self.get(key)) { Some(CV::Integer(i, path)) => { Ok(Some(Value { @@ -311,6 +337,7 @@ pub struct Value { pub enum Definition { Path(PathBuf), + Environment, } impl fmt::Debug for ConfigValue { @@ -496,9 +523,10 @@ impl ConfigValue { } impl Definition { - pub fn root<'a>(&'a self, _config: &'a Config) -> &'a Path { + pub fn root<'a>(&'a self, config: &'a Config) -> &'a Path { match *self { Definition::Path(ref p) => p.parent().unwrap().parent().unwrap(), + Definition::Environment => config.cwd(), } } } @@ -507,6 +535,7 @@ impl fmt::Display for Definition { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Definition::Path(ref p) => p.display().fmt(f), + Definition::Environment => "the environment".fmt(f), } } } diff --git a/src/cargo/util/errors.rs b/src/cargo/util/errors.rs index 255d8699e..d69d6b300 100644 --- a/src/cargo/util/errors.rs +++ b/src/cargo/util/errors.rs @@ -2,8 +2,10 @@ use std::error::Error; use std::ffi; use std::fmt; use std::io; +use std::num; use std::process::{Output, ExitStatus}; use std::str; +use std::string; use curl; use git2; @@ -308,6 +310,13 @@ from_error! { toml::DecodeError, ffi::NulError, term::Error, + num::ParseIntError, +} + +impl From for Box { + fn from(t: string::ParseError) -> Box { + match t {} + } } impl From> for Box { @@ -328,6 +337,7 @@ impl CargoError for toml::DecodeError {} impl CargoError for url::ParseError {} impl CargoError for ffi::NulError {} impl CargoError for term::Error {} +impl CargoError for num::ParseIntError {} // ============================================================================= // Construction helpers diff --git a/src/doc/config.md b/src/doc/config.md index d911e87bf..157f08172 100644 --- a/src/doc/config.md +++ b/src/doc/config.md @@ -90,8 +90,16 @@ target-dir = "target" # path of where to place all generated artifacts # Environment Variables -Cargo recognizes a few global [environment variables][env] to configure itself. -Settings specified via config files take precedence over those specified via +Cargo can also be configured through environment variables in addition to the +TOML syntax above. For each configuration key above of the form `foo.bar` the +environment variable `CARGO_FOO_BAR` can also be used to define the value. For +example the `build.jobs` key can also be defined by `CARGO_BUILD_JOBS`. + +Environment variables will take precedent over TOML configuration, and currently +only integer, boolean, and string keys are supported to be defined by environment variables. +In addition to the system above, Cargo recognizes a few other specific +[environment variables][env]. + [env]: environment-variables.html diff --git a/tests/test_cargo_config.rs b/tests/test_cargo_config.rs new file mode 100644 index 000000000..6de6aad52 --- /dev/null +++ b/tests/test_cargo_config.rs @@ -0,0 +1,26 @@ +use support::{project, execs}; +use hamcrest::assert_that; + +fn setup() { +} + +test!(read_env_vars_for_config { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + build = "build.rs" + "#) + .file("src/lib.rs", "") + .file("build.rs", r#" + use std::env; + fn main() { + assert_eq!(env::var("NUM_JOBS").unwrap(), "100"); + } + "#); + + assert_that(p.cargo_process("build").env("CARGO_BUILD_JOBS", "100"), + execs().with_status(0)); +}); diff --git a/tests/tests.rs b/tests/tests.rs index b67e02a0f..6b252a3e2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -66,6 +66,7 @@ mod test_cargo_rustdoc; mod test_cargo_search; mod test_cargo_test; mod test_cargo_tool_paths; +mod test_cargo_config; mod test_cargo_verify_project; mod test_cargo_version; mod test_shell; -- 2.30.2